﻿/*************************************************************************************
 * 工    具：  HK_FrameSequence
 * 
 * 描    述：  HK_FrameSequence 是一个进阶版的 Unity 序列帧动画控制器，
 *            支持三段式动画流程（初始→循环→结束），内置动画状态机自动处理阶段转换。
 *            可自动适配 UI Image 和 SpriteRenderer 双渲染模式，提供精准帧率控制与循环间隔暂停功能。
 *            提供一键式纹理优化功能（关闭 Mipmap/ReadWrite），并通过自动补充渲染组件和首帧同步简化使用流程。
 * 
 * 版    本：  V1.2
 * 作    者：  京产肠饭
 * 
 * 创    建：  2022/07/30  V1.0
 *
 * 更    新：  2025/03/31  V1.1
 *             1.性能优化：将所有 List<> 替换为 数组，提高性能，并缓存数组长度，减少频繁访问
 *             2.功能优化：优化帧率计算方式，帧间隔更精确
 *             3.功能新增：同时支持 Image 和 SpriteRenderer 组件，提高兼容性
 *             4.功能优化：新增 Hierarchy 中的图标绘制，提供易用性
 *
 *             2025/06/04  V1.2
 *             1.Bug修复：frameRate 可能被误设为负值，导致计算时报除零错误，在Inspector中限制输入范围，最小为1f
 *             2.功能新增：当手动给 frame_List 赋值时，自动同步首帧到渲染组件，避免手动设置
 *             3.功能新增：当给一个物体添加组件时，自动补充 Image 或 SpriteRenderer 组件，避免手动添加
 *             4.功能新增：新增“关闭所有序列帧的 Mipmap”按钮和“禁用所有序列帧的 Read/Write Enabled”，批量禁用关联纹理的Mipmap和Read/Write属性
 * 
 * 链    接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

namespace HKTools
{
    public class HK_FrameSequence : MonoBehaviour
    {
        [Header("是否自动播放："), SerializeField] bool isAutoPlay = false;
        [Header("是否循环播放："), SerializeField] bool isLoop = false;
        [Header("循环停顿时间："), SerializeField, Min(0f)] float loopPauseTime;
        [Header("动画帧率："), SerializeField, Min(1f)] float frameRate = 25.0f;

        [Header("初始序列帧："), SerializeField] public Sprite[] inFrames;
        [Header("循环序列帧："), SerializeField] public Sprite[] circleFrames;
        [Header("结束序列帧："), SerializeField] public Sprite[] outFrames;

        // 当前的序列帧动画
        Sprite[] curFrames;
        int curFramesCount = 0;

        enum AnimType
        {
            In,
            Circle,
            Out
        }

        AnimType animType;

        Image image;
        SpriteRenderer spriteRenderer;
        bool isUseImage; // 组件类型标记

        bool IsPlaying { get; set; }

        int frameIndex = 0;
        float timer;

#if UNITY_EDITOR
        /// <summary>
        /// 编辑器模式下添加组件时自动补充渲染组件
        /// </summary>
        void Reset()
        {
            // 检测是否在Canvas下（包含父级）
            bool isInCanvas = GetComponentInParent<Canvas>() != null;

            if (isInCanvas)
            {
                // 无Image组件时自动添加（支持撤销）
                if (GetComponent<Image>() == null)
                {
                    UnityEditor.Undo.AddComponent<Image>(gameObject);
                }
            }
            else
            {
                // 无SpriteRenderer组件时自动添加（支持撤销）
                if (GetComponent<SpriteRenderer>() == null)
                {
                    UnityEditor.Undo.AddComponent<SpriteRenderer>(gameObject);
                }
            }
        }
#endif

#if UNITY_EDITOR
        /// <summary>
        /// 编辑器模式下自动同步首帧到渲染组件
        /// </summary>
        void OnValidate()
        {
            // 如果所有序列帧都未赋值则返回
            if ((inFrames == null || inFrames.Length == 0)
            && (circleFrames == null || circleFrames.Length == 0)
            && (outFrames == null || outFrames.Length == 0))
                return;

            // 确定目标序列帧：优先inFrames，其次circleFrames
            Sprite[] targetFrames = inFrames.Length > 0 ? inFrames : circleFrames;

            // 重新获取当前组件（避免Awake前变量未初始化的问题）
            Image currentImage = GetComponent<Image>();
            SpriteRenderer currentSpriteRenderer = GetComponent<SpriteRenderer>();

            // 优先处理Image组件（与Awake逻辑保持一致）
            if (currentImage != null && targetFrames.Length > 0)
            {
                currentImage.sprite = targetFrames[0];
            }
            // 若没有Image组件，处理SpriteRenderer
            else if (currentSpriteRenderer != null && targetFrames.Length > 0)
            {
                currentSpriteRenderer.sprite = targetFrames[0];
            }
        }
#endif

        /// <summary>
        /// 初始化组件并设置初始帧显示
        /// </summary>
        void Awake()
        {
            image = GetComponent<Image>();
            spriteRenderer = GetComponent<SpriteRenderer>();

            isUseImage = image != null;

            if (inFrames != null && inFrames.Length > 0)
            {
                if (isUseImage)
                    image.sprite = inFrames[frameIndex];
                else
                    spriteRenderer.sprite = inFrames[frameIndex];
            }
            else if (circleFrames != null && circleFrames.Length > 0)
            {
                if (isUseImage)
                    image.sprite = circleFrames[frameIndex];
                else
                    spriteRenderer.sprite = circleFrames[frameIndex];
            }
            else
            {
                HKDebug.LogError("必须至少包含初始或循环序列帧");
            }

            IsPlaying = false;

            if (isAutoPlay)
                StartFramesAnim(isLoop);
        }

        /// <summary>
        /// 驱动动画更新的主循环
        /// </summary>
        void Update()
        {
            if (!IsPlaying)
                return;

            // 使用更精确的帧时间计算
            float targetDelta = 1f / frameRate;

            if ((timer += Time.deltaTime) < targetDelta)
                return;

            timer -= targetDelta;  // 保留余数用于下次计算

            frameIndex++;

            // 更新画面显示
            if (isUseImage)
                image.sprite = curFrames[frameIndex];
            else
                spriteRenderer.sprite = curFrames[frameIndex];

            // 处理不同动画阶段的逻辑转换
            switch (animType)
            {
                case AnimType.In:   // 初始动画阶段
                    if (frameIndex >= curFramesCount - 1)
                    {
                        animType = AnimType.Circle;
                        frameIndex = 0;
                        curFrames = circleFrames;
                        curFramesCount = curFrames.Length;
                    }
                    break;
                case AnimType.Circle:   // 循环动画阶段
                    if (frameIndex >= curFramesCount - 1)
                    {
                        if (!isLoop)
                        {
                            animType = AnimType.Out;
                            frameIndex = 0;
                            curFrames = outFrames;
                            curFramesCount = curFrames.Length;
                        }

                        if (loopPauseTime > 0f)
                        {
                            IsPlaying = false;
                            StartCoroutine(AnimPause_IE());
                        }
                        else
                        {
                            frameIndex = 0;
                        }
                    }
                    break;
                case AnimType.Out:  // 结束动画阶段
                    if (frameIndex >= curFramesCount - 1)
                    {
                        IsPlaying = false;
                    }
                    break;
            }
        }

        /// <summary>
        /// 循环动画间隔暂停协程
        /// </summary>
        IEnumerator AnimPause_IE()
        {
            yield return new WaitForSeconds(loopPauseTime);
            IsPlaying = true;
            frameIndex = 0;
        }

        /// <summary>
        /// 开始播放动画（可控制循环模式）
        /// </summary>
        /// <param name="isLoop">是否循环播放</param>
        public void StartFramesAnim(bool isLoop)
        {
            // 优先使用初始动画序列
            if (inFrames.Length > 0)
            {
                curFrames = inFrames;
                curFramesCount = curFrames.Length;
                animType = AnimType.In;
            }
            else // 否则 赋值为 CircleFrame序列帧
            {
                curFrames = circleFrames;
                curFramesCount = curFrames.Length;
                animType = AnimType.Circle;
            }

            this.isLoop = isLoop;

            if (curFramesCount > 0)
            {
                if (isUseImage)
                    image.sprite = curFrames[0];
                else
                    spriteRenderer.sprite = curFrames[0];

                frameIndex = 0;
                IsPlaying = true;
            }
            else
            {
                HKDebug.LogError("当前UI物体" + gameObject.name + "没有初始化或循环序列帧");
            }
        }

        /// <summary>
        /// 停止动画并播放结束序列（如果有）
        /// </summary>
        public void StopFramesAnim()
        {
            // 如果有结束动画则播放，否则立即停止
            if (outFrames.Length > 0)
            {
                curFrames = outFrames;
                curFramesCount = curFrames.Length;

                if (isUseImage)
                    image.sprite = curFrames[0];
                else
                    spriteRenderer.sprite = curFrames[0];

                frameIndex = 0;
                animType = AnimType.Out;
                IsPlaying = true;
            }
            else
            {
                IsPlaying = false;
            }
        }
    }
}